package cn.edu.lsnu.mbti.service.impl;

import cn.edu.lsnu.mbti.common.Constants;
import cn.edu.lsnu.mbti.common.R;
import cn.edu.lsnu.mbti.dto.UserDto;
import cn.edu.lsnu.mbti.entity.*;
import cn.edu.lsnu.mbti.entity.vo.UserVo;
import cn.edu.lsnu.mbti.exception.CustomerException;
import cn.edu.lsnu.mbti.mapper.PageUserMapper;
import cn.edu.lsnu.mbti.mapper.UniversityUserMapper;
import cn.edu.lsnu.mbti.mapper.UserMapper;
import cn.edu.lsnu.mbti.service.*;
import cn.edu.lsnu.mbti.util.JwtUtil;
import cn.edu.lsnu.mbti.util.MD5Utils;
import cn.edu.lsnu.mbti.util.RedisUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import java.util.stream.Collectors;

import static cn.edu.lsnu.mbti.common.RedisConstants.USER_KEY;

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private PersonalityService personalityService;
    @Resource
    private RegionService regionService;
    @Resource
    private MajorService majorService;
    @Resource
    private DepartmentService departmentService;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    @Lazy
    private UniversityService universityService;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private UserMapper userMapper;
    @Resource
    private PageUserMapper pageUserMapper;
    @Resource
    private UniversityUserMapper universityUserMapper;


    /**
     * vue后台管理修改用户状态
     * @param user
     */
    @Override
    @Transactional
    public void updateUserStatus(User user) {
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(User::getStatus,user.getStatus());
        updateWrapper.eq(User::getId,user.getId());
        try {
            userMapper.update(user,updateWrapper);
        } catch (Exception e) {
            throw new CustomerException("修改失败，请稍后重试");
        }
    }

    /**
     * vue后台管理删除用户
     * @param id
     * @return
     */
    @Override
    @Transactional
    public String deleteById(String id) {
        try {
            //先判断要删除的用户是否存在
            User user = userMapper.selectById(id);
            if (BeanUtil.isEmpty(user)){
                throw new CustomerException("该用户不存在");
            }
            //删除关联表
            //pageUser
            LambdaQueryWrapper<PageUser> psWrapper = new LambdaQueryWrapper<>();
            psWrapper.eq(StrUtil.isNotBlank(id),PageUser::getUserId,id);
            pageUserMapper.delete(psWrapper);

            //universityUser
            LambdaQueryWrapper<UniversityUser> usWrapper = new LambdaQueryWrapper<>();
            usWrapper.eq(StrUtil.isNotBlank(id),UniversityUser::getUserId,id);
            universityUserMapper.delete(usWrapper);

            //删除用户
            this.removeById(id);

            return "删除成功";
        } catch (CustomerException e) {
            throw new CustomerException("删除失败，请稍后重试");
        }
    }

    /**
     * 保存用户
     * @param user
     * @return
     */
    @Override
    @Transactional
    public UserDto saveUser(User user) {
        try {
            //判断id是否为空
            if (StrUtil.isBlank(user.getId())){
                //空，新增用户
                //设置默认密码
                user.setPassword(MD5Utils.getMD5("123456"));

                userMapper.insert(user);
            }else{
                //不为空，修改用户
                userMapper.updateById(user);
            }
            //设置属性
            UserDto userDto = convertUserToUserDto(user);
            return userDto;
        } catch (Exception e) {
            throw new CustomerException("保存失败，请稍后重试");
        }
    }

    /**
     * 分页查询用户列表
     * @param userVo
     * @return
     */
    @Override
    public Page<UserDto> pageList(UserVo userVo) {
        //构造查询条件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(User::getUsername,userVo.getUsername());
        wrapper.like(User::getEmail,userVo.getEmail());
        wrapper.eq(StrUtil.isNotBlank(userVo.getGender()),User::getGender,userVo.getGender());
        wrapper.eq(ObjectUtil.isNotNull(userVo.getStatus()),User::getStatus,userVo.getStatus());
        wrapper.eq(StrUtil.isNotBlank(userVo.getPersonalityId()),User::getPersonalityId,userVo.getPersonalityId());
        wrapper.eq(StrUtil.isNotBlank(userVo.getMajorId()),User::getMajorId,userVo.getMajorId());
        wrapper.eq(StrUtil.isNotBlank(userVo.getRegionId()),User::getRegionId,userVo.getRegionId());
        wrapper.orderByDesc(User::getCreateTime);
        Page<User> userPage = new Page<>(userVo.getPageNum(), userVo.getPageSize());

        //分页查询
        userPage = userMapper.selectPage(userPage,wrapper);

        //对象复制
        Page<UserDto> userDtoPage = new Page<>();
        BeanUtils.copyProperties(userPage,userDtoPage,"records");

        //封装属性
        userDtoPage.setRecords(userPage.getRecords().stream().map(this::convertUserToUserDto)
            .collect(Collectors.toList()));

        return userDtoPage;
    }

    /**
     * 用户做完mbti测试 根据id修改用户的人格数据
     *
     * @return
     */
    @Transactional
    @Override
    public UserDto addUserPersonality(UserDto userDto) {
        //清除redis中已存在的推荐数据
        redisUtil.del(DepartmentServiceImpl.recommendDepartmentKey + userDto.getId());
        redisUtil.del(UniversityServiceImpl.recommendUniversityKey + userDto.getId());

        try {
            //根据occupation查询对应的personality
            LambdaQueryWrapper<Personality> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Personality::getCode, userDto.getPersonalityCode());
            Personality personality = personalityService.getOne(wrapper);

            //根据personality获取id并存到用户信息中
            //修改用户的personality_id
            userMapper.updatePersonalityIdById(personality.getId(), userDto.getId());
            //重新设置userDto的值
            userDto.setPersonalityId(personality.getId());
            userDto.setPersonalityCode(personality.getCode());
            userDto.setPersonalityDescription(personality.getDescription());
            userDto.setPersonalityName(personality.getName());

            //将用户保存到redis
            redisUtil.set(USER_KEY + userDto.getId(), userDto.getId(), Constants.EXPIRE_WEEK);

        } catch (Exception e) {
            throw new CustomerException("测试失败，请稍后重试");
        }

        return userDto;
    }

    /**
     * 修改数据
     * 清空缓存
     *
     * @param user
     * @return
     */
    @Transactional
    @Override
    public R<String> edit(User user) {
        try {
            this.updateById(user);
            //清空缓存
            redisUtil.del(USER_KEY);
//           throw new RuntimeException("异常发生了，回滚事务");
        } catch (Exception e) {
            e.printStackTrace();
            return R.error("修改失败");
        }
        return R.success("修改成功");
    }

    /**
     * 完善个人信息（回显数据）
     * 从redis中查询是否有key，有就返回redis，没有再重新查询
     *
     * @param id
     * @return
     */
    @Override
    public UserDto selectForEdit(String id) {
        UserDto userDto = new UserDto();
        //查询redis中是否存在userKey
        String jsonStr = (String) redisUtil.get(USER_KEY);
        //判断jsonStr是否有数据
        if (StrUtil.isNotBlank(jsonStr)) {
            //不为空，redis有数据
            //直接返回redis中的数据
            //将字符串转换为对象
            userDto = JSONUtil.toBean(jsonStr, new TypeReference<UserDto>() {
            }, true);
            return userDto;
        }
        //redis中没有数据，从数据库中查询，然后保存到redis中
        //1.查询用户基本信息
        User user = this.getById(id);

        //2.1对象复制
        BeanUtils.copyProperties(user, userDto);

        //3.查询personalityName
        //3.1 获取personalityId
        String personalityId = user.getPersonalityId();
        if (StringUtils.isNotEmpty(personalityId)) {
            //3.2查询
            Personality personality = personalityService.getById(personalityId);
            //3.3封装personality
            userDto.setPersonalityName(personality.getName());
            userDto.setPersonalityCode(personality.getCode());
        }

        //4.查询regionName
        //4.1获取regionId
        String regionId = user.getRegionId();
        if (StringUtils.isNotEmpty(regionId)) {
            //4.2查询
            Region region = regionService.getById(regionId);
            //4.3封装
            userDto.setRegionName(region.getName());
        }

        //5.查询majorName
        //5.majorId
        String majorId = user.getMajorId();
        if (StringUtils.isNotEmpty(majorId)) {
            //5.2查询
            Major major = majorService.getById(majorId);
            //5.3封装
            userDto.setMajorName(major.getName());
        }

        //6.查询expectDepartmentName
        //6.expectDepartmentId
        String expectDepartmentId = user.getExpectDepartmentId();
        if (StringUtils.isNotEmpty(expectDepartmentId)) {
            //6.2查询
            Department department = departmentService.getById(expectDepartmentId);
            //6.3封装
            userDto.setExpectDepartmentName(department.getName());
        }

        //7.查询universityName
        String universityId = user.getSchool();
        if (StrUtil.isNotBlank(universityId)) {
            //查询
            University university = universityService.getById(universityId);
            //封装
            userDto.setUniversityName(university.getName());
        }
        //将userDto保存到redis中
        redisUtil.set(USER_KEY + id, JSONUtil.toJsonStr(userDto), Constants.EXPIRE_WEEK);


        return userDto;
    }

    /**
     * 登出
     * 清楚缓存
     *
     * @return
     */
    @Override
    public R<String> logout(String id) {
        //清除缓存
        redisUtil.del(USER_KEY + id);
        return R.success("登出成功");
    }

    /**
     * 注册
     *
     * @param user
     * @return
     */
    @Transactional
    @Override
    public R<User> register(User user) {
        //1.密码进行md5加密
        user.setPassword(MD5Utils.getMD5(user.getPassword()));

        //2.对用户信息进行保存
        this.save(user);

        return R.success(user);
    }

    /**
     * 注册时根据用户名查询用户
     *
     * @param user
     * @return
     */
    @Override
    public R<String> getOneByUsername(User user) {
        //1.构造查询构造器
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, user.getUsername());

        //2.查询用户user
        User one = this.getOne(queryWrapper);

        //3.判断用户是否存在
        if (one != null) {
            //用户存在，不能注册
            return R.error("用户已存在，请更换");
        }

        //用户不存在，可以注册
        return R.success("用户可用");
    }

    /**
     * 用户登录
     *
     * @param user
     * @return
     */
    @Override
    public UserDto login(User user) {
        //0.对密码加密
        user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes()));

        //1.构造查询条件
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, user.getUsername());
        queryWrapper.eq(User::getPassword, user.getPassword());

        //2.查询对象
        User one = this.getOne(queryWrapper);
        UserDto userDto = new UserDto();

        //3.判断user是否为空
        if (one != null) {
            BeanUtils.copyProperties(one, userDto);
            //4.判断状态
            Integer status = one.getStatus();
            if (status == 1) {
                //未被锁定，登录成功
                //封装userDto
                //4.1 token
                String token = JwtUtil.generateToken(one.getUsername());
                userDto.setToken(token);
                //4.2 personalityName
                //查询一次数据库
                if (StrUtil.isNotBlank(one.getPersonalityId())) {
                    Personality personality = personalityService.getById(one.getPersonalityId());
                    if (ObjectUtil.isNotNull(personality)) {
                        userDto.setPersonalityName(personality.getName());
                        //4.3 personalityCode
                        userDto.setPersonalityCode(personality.getCode());
                        //personalityDescription
                        userDto.setPersonalityDescription(personality.getDescription());
                    }
                }
                //4.4 regionName
                if (StrUtil.isNotBlank(one.getRegionId())) {
                    Region region = regionService.getById(one.getRegionId());
                    if(BeanUtil.isNotEmpty(region)){
                        userDto.setRegionName(region.getName());
                    }
                }
                //4.5 majorName
                if (StrUtil.isNotBlank(one.getMajorId())) {
                    Major major = majorService.getById(one.getMajorId());
                    if(BeanUtil.isNotEmpty(major)){
                        userDto.setMajorName(major.getName());
                    }
                }
                //4.6 expectDepartmentName
                if (StrUtil.isNotBlank(one.getExpectDepartmentId())) {
                    Department department = departmentService.getById(one.getExpectDepartmentId());
                    if(BeanUtil.isNotEmpty(department)){
                        userDto.setExpectDepartmentName(department.getName());
                    }
                }
                //4.7 universityName
                if (StrUtil.isNotBlank(one.getSchool())) {
                    University university = universityService.getById(one.getSchool());
                    if(BeanUtil.isNotEmpty(university)){
                        userDto.setUniversityName(university.getName());
                    }
                }
                //将用户信息存到redis中，存放七天
                redisUtil.set(USER_KEY + one.getId(), JSONUtil.toJsonStr(userDto), Constants.EXPIRE_WEEK);

                //对密码做处理
                userDto.setPassword(null);
                return userDto;
            } else {
                //账号已被锁定，登录失败
                throw new CustomerException("账号已被锁定，禁止登录");
            }
        } else {
            //user为空，数据库中没有改用户的信息
            throw new CustomerException("用户名或密码错误");
        }

    }

    //将user转换为userDto
    private UserDto convertUserToUserDto(User user){
        UserDto userDto = new UserDto();
        BeanUtils.copyProperties(user,userDto);
        if (StrUtil.isNotBlank(user.getPersonalityId())){
            Personality personality = personalityService.getById(user.getPersonalityId());
            if (BeanUtil.isNotEmpty(personality)){
                userDto.setPersonalityName(personality.getName());
                userDto.setPersonalityCode(personality.getCode());
                userDto.setPersonalityDescription(personality.getDescription());
            }
        }
        if (StrUtil.isNotBlank(user.getRegionId())){
            Region region = regionService.getById(user.getRegionId());
            if (BeanUtil.isNotEmpty(region)){
                userDto.setRegionName(region.getName());
            }
        }
        if (StrUtil.isNotBlank(user.getMajorId())){
            Major major = majorService.getById(user.getMajorId());
            if (BeanUtil.isNotEmpty(major)){
                userDto.setMajorName(major.getName());
            }
        }
        if (StrUtil.isNotBlank(user.getExpectDepartmentId())){
            Department department = departmentService.getById(user.getExpectDepartmentId());
            if (BeanUtil.isNotEmpty(department)){
                userDto.setExpectDepartmentName(department.getName());
            }
        }
        if (StrUtil.isNotBlank(user.getSchool())){
            University university = universityService.getById(user.getSchool());
            if (BeanUtil.isNotEmpty(university)){
                userDto.setUniversityName(university.getName());
            }
        }

        return userDto;
    }

}
